/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.logging.Level;
import java.util.logging.Logger;
import security.Base64Coder;
import server.data.User;

/**
 * Listens on the user's 2nd TCP-port for incoming timestamp requests on server-outage.
 * @author Gruppe 95
 */
public class ServerOutageThread implements Runnable, Stoppable {

    private static final Logger logger = Logger.getLogger("ServerOutageThread");
    private static final String OUT_TIMESTAMP = "!timestamp";
    private boolean bStopped = false;
    private boolean sending = false;
    private PrivateKey privateKey;
    private ServerSocket listenSocket;
    private Socket requestSocket;
    private PrintWriter out;
    private BufferedReader in;

    public ServerOutageThread(User user, PrivateKey privateKey) throws IOException {
        System.out.println(user.getiPClientPort());
        listenSocket = new ServerSocket(user.getiPClientPort());
        this.privateKey = privateKey;
    }

    @Override
    public void run() {
        while (!bStopped) {
            if (!sending) {
            try {
                sending = true;
                System.out.println("ServerOutageThread listen for requests");
                requestSocket = listenSocket.accept();
                logger.log(Level.INFO, "new requstSocket {0} ", new Object[]{requestSocket.getInetAddress().toString()});
                out = new PrintWriter(requestSocket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(requestSocket.getInputStream()));
                sendTimestamp();
                //@todo: send stuff
            } catch (IOException ex) {
                Logger.getLogger(ServerOutageThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        }
    }

    @Override
    public synchronized void stop() {
        bStopped = true;
        try {
            listenSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(ServerOutageThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Sends a signed timestamp via the current socket-connection.
     * @throws IOException 
     */
    private void sendTimestamp() throws IOException {
        String line;
        if ((line = in.readLine()) != null) {
            logger.log(Level.INFO, "timestamp request: {0} ", new Object[]{line});
            out.println(generateTimestamp(line));
        }
        out.close();
        in.close();
        requestSocket.close();
        sending = false;
    }

    /**
     * Generates the timestamp-string to be send.
     * @param input
     * @return 
     */
    private String generateTimestamp(String input) {
        String[] parts = input.split(" ");
        String out = OUT_TIMESTAMP + " " + parts[1] + " " + parts[2] + " " + System.currentTimeMillis();
        String sigString =  generateSignature(out);
        out = out + " " + sigString;
        logger.log(Level.INFO, "signed timestamp message: {0} ", new Object[]{out});
        return out;
    }
    
    /**
     * Generates a signature of the timestamp-string signed with the user's private key.
     * @param input
     * @return 
     */
    private String generateSignature(String input) {
        byte[] output = null;
        try {
            Signature signature = Signature.getInstance("SHA512withRSA");
                signature.initSign(privateKey);
                signature.update(input.getBytes());
                output = signature.sign();
            
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ServerOutageThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
                Logger.getLogger(ServerOutageThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SignatureException ex) {
                Logger.getLogger(ServerOutageThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        logger.log(Level.INFO, "generated signature: {0} ", new Object[]{Base64Coder.base64encodeParts(new String(output))});
        return Base64Coder.base64encodeParts(new String(output));
    }
}
